package com.avo.ramcache;

import com.avo.ramcache.anno.core.Cached;
import com.avo.ramcache.config.IEntity;
import com.avo.ramcache.orm.MappingModel;
import com.avo.ramcache.persist.PersisterConfig;
import com.avo.ramcache.persist.PersisterType;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import java.util.*;

@Getter
@Setter
@ConfigurationProperties("ram.cache")
public class RamCacheProperties {

    protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    private final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    private String dbName;

    private MappingModel mappingModel;

    private CacheManagerConfig config;

    private List<Persist> persists;

    private EntityInfo entity;

    public HashMap<String, PersisterConfig> getPersisterConfigs() {
        HashMap<String, PersisterConfig> persisterConfigs = new HashMap<>();
        getPersists().forEach(persist -> {
            persisterConfigs.put(persist.getName(), new PersisterConfig(persist.getType(), persist.getParam()));
        });
        return persisterConfigs;
    }

    public Map<String, Integer> getCacheSizeMapping() {
        return getConfig().getCacheSizeMapping();
    }

    public Set<Class<IEntity<?>>> getEntityClasses() {
        Set<Class<IEntity<?>>> classes = new HashSet<>();
        List<String> classNames = entity.getClassNames();
        // 收集class
        if (classNames != null && !classNames.isEmpty()) {
            classNames.forEach(className -> {
                try {
                    Class clz = Class.forName(className);
                    classes.add(clz);
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException("无法获取的资源类" + className, e);
                }
            });
        }
        String packageName = getEntity().getPackagePath();
        if (StringUtils.isNotBlank(packageName)) {
            Set<String> classNameSet = getResources(packageName);
            classNameSet.forEach(className -> {
                try {
                    Class clz = Class.forName(className);
                    classes.add(clz);
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException("无法获取的资源类" + className, e);
                }
            });
        }
        return classes;
    }

    @Getter
    @Setter
    public static class CacheManagerConfig {

        private boolean lockAspect;

        private Map<String, Integer> cacheSizeMapping;

    }

    @Getter
    @Setter
    public static class Persist {

        private String name;

        private PersisterType type;

        private String param;
    }

    @Getter
    @Setter
    public static class EntityInfo {

        private String packagePath;

        private List<String> classNames;
    }

    private String resolveBasePackage(String basePackageName) {
        return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackageName));
    }

    private Set<String> getResources(String packageName) {
        try {
            String packageSearchPath = "classpath*:" + this.resolveBasePackage(packageName) + "/" + DEFAULT_RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
            Set<String> result = new HashSet<>();
            String name = Cached.class.getName();
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                    AnnotationMetadata annoMeta = metadataReader.getAnnotationMetadata();
                    if (annoMeta.hasAnnotation(name)) {
                        ClassMetadata clzMeta = metadataReader.getClassMetadata();
                        result.add(clzMeta.getClassName());
                    }
                }
            }
            return result;
        } catch (Exception e) {
            throw new RuntimeException("无法读取资源信息");
        }
    }
}
